#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>

#include <psp2/kernel/processmgr.h>

#include <SDL2/SDL.h>
#include <smpeg2/smpeg.h>

#include "utils.h"
#include "patch.h"
#include "avplayer.h"

typedef struct cBinker
{
    int32_t unk0_256[256];    // +0
    SMPEG_Frame *smpeg_frame; // +256   SMPEG frame
    int32_t unk_257;          // +257
    SDL_mutex *lock;          // +258   帧数据操作互斥锁
    SMPEG_Info smpeg_info;    // +260   SMPEG info (4*34=136)
    SMPEGstatus smpeg_status; // +296   SMPEG status (SMPEG_ERROR=-1, SMPEG_STOPPED=0, SMPEG_PLAYING=1)
    SMPEG *smpeg;             // +297   SMPEG object
    int32_t unk_298;          // +298
    int32_t unk_299;          // +299
    int32_t unk_300;          // +300
    int32_t unk_301;          // +301
    int32_t unk_302;          // +302
    SDL_Texture *texture;     // +303   pixel_formart: SDL_PIXELFORMAT_YV12 (842094169)
    int32_t unk_304;          // +304
    int8_t video_pause;       // +1220 (+305)  Video pause
    int8_t video_ready;       // +1221         Video started?
    int8_t unk_1222;          // +1222
    int8_t unk_1223;          // +1223
    int32_t audio_volume;     // +306   音量
    int32_t unk_307;          // +307
} cBinker;                    // sizeo: 1232 4*308

int (*BinkStartPlayBack)(char **a1, uint16_t *a2);
int (*cBinker_bik_Open)(void *this, const char *filename, int a3, int volume);

int j_BinkStartPlayBack(char **a1, uint16_t *a2)
{
    char *ori_rolebik = "rolebik.bik";
    char *new_rolebik = "rolebik.BIK";
    int len = strlen(ori_rolebik);
    char *filename = *a1 + 2;

    if (strncmp(filename, ori_rolebik, len) == 0)
    {
        char ch = filename[len];
        filename[len] = '\0';
        debugPrintf("j_BinkStartPlayBack: \"%s\"=>\"%s\"\n", filename, new_rolebik);
        strcpy(filename, new_rolebik);
        filename[len] = ch;
    }

    return BinkStartPlayBack(a1, a2);
}

int j_cBinker_bik_Open(cBinker *this, const char *filename, int a3, int volume)
{
    if (!filename)
        return 0;

    debugPrintf("j_cBinker_bik_Open: filename: %s, volume: %d\n", filename, volume);
    // debugPrintf("this->smpeg: %p, this->texture: %p, this->lock: %p\n", this->smpeg, this->texture, this->lock);

    void *FileIO = (void *)(FILEIO_ADDRESS);
    char *video_path = fileIO_GetVideoPath(FileIO, filename);
    if (!video_path || !file_exists(video_path))
        return -1;

    char *p = strrchr(video_path, '.');
    if (p++ && strcasecmp(p, "mp4") == 0)
    {
        if (AvplayeroOpen(video_path) >= 0)
        {
            int vol = volume & ~(volume >> 31);
            if (vol >= 127)
                vol = 127;
            vol = ((float)vol / 127.0) * 100.0;
            AvplayerSetvolume(vol);

            while (AvplayerIsActive())
            {
                // Disable "auto suspend && oled off && oled dimming"
                sceKernelPowerTick(SCE_KERNEL_POWER_TICK_DISABLE_AUTO_SUSPEND | SCE_KERNEL_POWER_TICK_DISABLE_OLED_OFF | SCE_KERNEL_POWER_TICK_DISABLE_OLED_DIMMING);
                AvplayerDrawFrame();
            }

            AvplayerClose();
        }
        return -1;
    }

    int ret = cBinker_bik_Open(this, filename, a3, volume);
    if (ret == -1)
    {
        // debugPrintf("this->smpeg: %p, this->texture: %p, this->lock: %p\n", this->smpeg, this->texture, this->lock);

        // 修正原程序未清除问题
        if (this->lock)
            SDL_DestroyMutex(this->lock);
        this->lock = NULL;
    }

    return cBinker_bik_Open(this, filename, a3, volume);
}

SMPEG *SMPEG_new_rwops_soloader(SDL_RWops *src, SMPEG_Info *info, int freesrc, int sdl_audio)
{
    if (src && SDL_RWsize(src) < 100 * 1024)
    {
        // 过滤nil.bik，它会导致程序卡，以文件大小判断
        printf("SMPEG_new_rwops: skip size: %llu\n", SDL_RWsize(src));
        SDL_RWclose(src);
        return NULL;
    }

    return SMPEG_new_rwops(src, info, freesrc, sdl_audio);
}

int initPatchBinker()
{
    BinkStartPlayBack = (void *)so_symbol(&so_mod, "_Z17BinkStartPlayBackPPcPt");
    cBinker_bik_Open = (void *)so_symbol(&so_mod, "_ZN7cBinker8bik_OpenEPKcii");

    hook_addr((uintptr_t)(LOAD_ADDRESS + 0x0010AB10), (uintptr_t)&j_BinkStartPlayBack);
    hook_addr((uintptr_t)(LOAD_ADDRESS + 0x001061D0), (uintptr_t)&j_cBinker_bik_Open);

    return 0;
}
